package cfsmon.logic.nfm;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import cfsmon.common.Event;
import cfsmon.common.Parameter;

public class Nfm implements INfm {

	private int maximumNumberOfMonitors;
	
	private Map<String, NfmState> states;
	private Set<NfmState> finalStates;
	private Set<NfmState> initialStates;
	
	private Map<String, Event> events;
	private Map<String, Parameter> parameters;
	
	public Nfm(){
		states = new HashMap<String, NfmState>();
		finalStates = new HashSet<NfmState>();
		initialStates = new HashSet<NfmState>();
		
		events = new HashMap<String, Event>();
		parameters = new HashMap<String, Parameter>();
		maximumNumberOfMonitors = 1;
	}
	
	public Nfm(
			Map<String, NfmState> states,
			Set<NfmState> initialStates,
			Set<NfmState> finalStates,
			Map<String, Event> events,
			Map<String, Parameter> parameters,
			int maximumNumberOfMonitors
			){
		this.states = states;
		this.finalStates = finalStates;
		this.initialStates = initialStates;
		this.events = events;
		this.parameters = parameters;
		this.maximumNumberOfMonitors = maximumNumberOfMonitors;
	}
	
	@Override
	public Map<String, NfmState> getStates() {
		return states;
	}

	@Override
	public Set<NfmState> getFinalStates() {
		return finalStates;
	}

	@Override
	public Set<NfmState> getInitialStates() {
		return initialStates;
	}

	@Override
	public Map<String, Event> getEvents() {
		return events;
	}

	@Override
	public Map<String, Parameter> getParameters() {
		return parameters;
	}

	@Override
	public int getMaximumNumberOfMonitors() {
		return maximumNumberOfMonitors;
	}

	@Override
	public void setMaximumNumberOfMonitors(int max) {
		assert(max > 0);
		maximumNumberOfMonitors = max;
	}

	@Override
	public void addState(NfmState state) {
		assert(state != null);
		if (states.get(state.getName()) == null){
			states.put(state.getName(), state);
			for (Event e : state.getTransitions().keySet()){
				addEvent(e);
			}
		}
	}

	@Override
	public void addFinalState(NfmState state) {
		addState(state);
		finalStates.add(state);
	}

	@Override
	public void addInitialState(NfmState state) {
		addState(state);
		initialStates.add(state);
	}

	@Override
	public void addEvent(Event event) {
		if (!events.containsKey(event.name)){
			events.put(event.name, event);
			for (Parameter p : event.parameters){
				addParameter(p);
			}			
		}
	}

	@Override
	public void addParameter(Parameter parameter) {
		if (parameter == null)
			throw new NullPointerException("Null is not permitted as parameter.");
		if (parameters.get(parameter.name) == null){
			parameters.put(parameter.name, parameter);
		} else {
			Parameter p = parameters.get(parameter.name);
			if (parameter.compareTo(p) != 0){
				throw new IllegalStateException("Ambiguous event parameters are not permitted.");
			}
		}
	}
	
	@Override
	public String toString(){
		StringBuffer sb = new StringBuffer();
		sb.append("Parameters := " + parameters.values().toString() + "\n");
		sb.append("Max monitors := " + maximumNumberOfMonitors + "\n");
		sb.append("States := " + states.keySet().toString() + "\n");
		sb.append("Events := " + events.values().toString() + "\n");
		sb.append("Initial states := " + initialStates.toString() + "\n");
		sb.append("Final states := " + finalStates.toString() + "\n");
		
		// print out transitions
		sb.append("Transitions := Delta U Delta' where\n");
		for (NfmState s : states.values()){
			sb.append(s.toStringDeep());
		}
		return sb.toString();
	}
	
	public void print(){
		System.out.println(toString());
	}
}
